package com.yanqu.road.server.manager.activity.hanghai;

import com.google.protobuf.AbstractMessage;
import com.yanqu.road.entity.activity.hanghai.HangHaiGoods;
import com.yanqu.road.entity.activity.hanghai.HangHaiPort;
import com.yanqu.road.entity.activity.hanghai.HangHaiUnionData;
import com.yanqu.road.entity.activity.hanghai.HangHaiUserData;
import com.yanqu.road.entity.activity.hanghai.config.HangHaiConfig;
import com.yanqu.road.entity.activity.hanghai.config.HangHaiGoodsConfig;
import com.yanqu.road.entity.activity.hanghai.config.HangHaiPortConfig;
import com.yanqu.road.entity.activity.hanghai.config.HangHaiShipConfig;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.log.LogHangHaiBuy;
import com.yanqu.road.entity.log.LogHangHaiMove;
import com.yanqu.road.entity.log.LogHangHaiReport;
import com.yanqu.road.entity.log.LogHangHaiReportReward;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.entity.union.activitygroup.UnionActivityGroupUnion;
import com.yanqu.road.logic.pb.PlayerBasePb;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.activity.HangHaiActivityProto;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.config.GoodsMgr;
import com.yanqu.road.server.manager.log.CrossAutoLogMgr;
import com.yanqu.road.server.manager.user.CrossUserMgr;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.utils.LocalDateTimeHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class CrossHangHaiActivity {

    private static Logger logger = LogManager.getLogger(CrossHangHaiActivity.class.getName());

    private final Object lock = new Object();

    // 分组相关
    private int groupId;
    private CrossHangHaiActivityGroup group;

    // 配置
    private HangHaiConfig config;

    // 不入库
    private Map<Long, Boolean> hadBuyMap = new ConcurrentHashMap<>();

    // ==================================================================================

    public CrossHangHaiActivity(CrossHangHaiActivityGroup tmpGroup, int tmpGroupId, HangHaiConfig tmpConfig) {
        group = tmpGroup;
        groupId = tmpGroupId;
        config = tmpConfig;
    }

    public HangHaiConfig getConfig() {
        return config;
    }

    public void reloadConfig(HangHaiConfig tmpConfig) {
        this.config = tmpConfig;
    }

    public HangHaiUserData getUserData(long userId) {
        HangHaiUserData userData = group.getUserDataMap().get(userId);
        if (userData != null) {
            checkUserData(userData);
        }
        return userData;
    }

    public HangHaiUnionData getUnionData(String unionUid) {
        // ID有问题
        if (StringUtils.isNullOrEmpty(unionUid)) {
            return null;
        }

        // 生成数据判断
        if (!group.getUnionDataMap().containsKey(unionUid)) {
            synchronized (lock) {
                if (!group.getUnionDataMap().containsKey(unionUid)) {
                    group.getUnionDataMap().put(unionUid, createUnionData(config, unionUid));
                }
            }
        }

        HangHaiUnionData unionData = group.getUnionDataMap().get(unionUid);
        if (unionData != null) {
            checkUnionData(unionData);
        }
        return unionData;
    }

    private void loginCreateUserData(long userId, HangHaiActivityProto.CrossHangHaiLoginReqMsg reqMsg) {
        if (!group.getUserDataMap().containsKey(userId)) {
            synchronized (lock) {
                if (!group.getUserDataMap().containsKey(userId)) {
                    group.getUserDataMap().put(userId, createUserData(userId, BigDecimal.valueOf(reqMsg.getInitMoney())));
                }
            }
        }
    }

    public boolean getHadBuy(long userId) {
        return hadBuyMap.getOrDefault(userId, true);
    }

    private void setHadBuy(long userId, boolean hadBuy) {
        hadBuyMap.put(userId, hadBuy);
    }

    private HangHaiUserData createUserData(long userId, BigDecimal initMoney) {
        HangHaiUserData userData = new HangHaiUserData();
        userData.setActivityId(config.getActivityId());
        userData.setUserId(userId);
        userData.setMoney(initMoney);
        userData.setGoodsMap(new ConcurrentHashMap<>());
        userData.setPortId(getRandomPortId(config));
        userData.setToPortId(0);
        userData.setFinishTime(0L);
        userData.setShipLevel(1);
        userData.setReportRewardList(new ArrayList<>());
        userData.setLastResetTime(System.currentTimeMillis());
        userData.setInsertOption();
        return userData;
    }

    public static int getRandomPortId(HangHaiConfig config) {
        List<HangHaiPortConfig> portConfigList = new ArrayList<>(config.getPortConfigMap().values());
        return portConfigList.get(new Random().nextInt(portConfigList.size())).getPortId();
    }

    private HangHaiUnionData createUnionData(HangHaiConfig config, String unionUid) {
        HangHaiUnionData unionData = new HangHaiUnionData();
        unionData.setActivityId(config.getActivityId());
        unionData.setLastResetTime(0);
        unionData.setUnionUid(unionUid);
        unionData.setPortMap(new ConcurrentHashMap<>());
//        unionData.setLockMemberMap(null);
        unionData.setInsertOption();
        // 初始化商会港口
        for (HangHaiPortConfig value : config.getPortConfigMap().values()) {
            HangHaiPort port = new HangHaiPort();
            port.setActivityId(config.getActivityId());
            port.setUnionUid(unionUid);
            port.setPortId(value.getPortId());
            port.setSpecId(value.getSpecGoodsId());
            port.setLackId(0);
            port.setGoodsMap(new ConcurrentHashMap<>());
            port.setInsertOption();
            unionData.getPortMap().put(port.getPortId(), port);
        }
        // 重置港口
        resetUnionDataOnInit(unionData);
        return unionData;
    }

    private void resetUnionData(HangHaiUnionData unionData) {

        synchronized (lock) {

            // 刷新时间判断
            if (!canResetData(config.getRefreshTimeList(), unionData.getLastResetTime())) {
                return;
            }

            Random random = new Random();

            // 检查港口Map
            if (unionData.getPortMap() == null) {
                unionData.setPortMap(new ConcurrentHashMap<>());
            }
            Map<Integer, HangHaiPort> portMap = unionData.getPortMap();
            // 检查港口Map数量
            Map<Integer, HangHaiPortConfig> portConfigMap = config.getPortConfigMap();
            for (HangHaiPortConfig portConfig : portConfigMap.values()) {
                if (!portMap.containsKey(portConfig.getPortId())) {
                    HangHaiPort port = new HangHaiPort();
                    port.setActivityId(config.getActivityId());
                    port.setUnionUid(unionData.getUnionUid());
                    port.setPortId(portConfig.getPortId());
                    port.setSpecId(portConfig.getSpecGoodsId());
                    port.setLackId(0);
                    port.setGoodsMap(new ConcurrentHashMap<>());
                    portMap.put(port.getPortId(), port);
                }
            }

            // 清零数据
            for (HangHaiPort port : portMap.values()) {
                // 检查特产id
                HangHaiPortConfig portConfig = portConfigMap.get(port.getPortId());
                port.setSpecId(portConfig == null ? 0 : portConfig.getSpecGoodsId());
                // 清空紧缺id
                port.setLackId(0);
                // 重置上报
                port.setReportUserId(0L);
            }

            // 设置紧缺ID（都用一样的random，且方法内随机的参数都一样，结果就一样了）
            long resetTime = getLastResetTime(config.getRefreshTimeList());
            Random lackRandom = getRandomByKey(config.getActivityId(), resetTime);
            initPortLackId(portMap, config, lackRandom);

            // 重置每个港口
            for (HangHaiPort hangHaiPort : portMap.values()) {

                // 生成一份货物
                Map<Integer, HangHaiGoods> goodsMap = new ConcurrentHashMap<>();
                for (HangHaiGoodsConfig goodsConfig : config.getGoodsConfigMap().values()) {
                    HangHaiGoods goods = new HangHaiGoods();
                    goods.setId(goodsConfig.getGoodsId());
                    if (goodsConfig.getGoodsId() == hangHaiPort.getSpecId()) {
                        // 特产
                        goods.setCount(calStockNum(config, goods.getId(), 1, unionData, random));
                        goods.setPrice(calPrice(config, goods.getId(), 1, random));
                    } else if (goodsConfig.getGoodsId() == hangHaiPort.getLackId()) {
                        // 紧缺
                        goods.setCount(calStockNum(config, goods.getId(), 3, unionData, random));
                        goods.setPrice(calPrice(config, goods.getId(), 3, random));
                    } else {
                        // 普通
                        goods.setCount(calStockNum(config, goods.getId(), 2, unionData, random));
                        goods.setPrice(calPrice(config, goods.getId(), 2, random));
                    }

                    goodsMap.put(goods.getId(), goods);
                }

                // 货物加入港口
                hangHaiPort.setGoodsMap(goodsMap);
                hangHaiPort.setUpdateOption();
            }

            unionData.setLastResetTime(resetTime);

        }

        syncDataToUnion(unionData);
        syncReportTimesToUnion(unionData);
        for (HangHaiPort port : unionData.getPortMap().values()) {
            syncPortToUnion(port, unionData.getLastResetTime(), 2);
        }
    }

    private void resetUnionDataOnInit(HangHaiUnionData unionData) {

        Random random = new Random();

        // 检查港口Map
        if (unionData.getPortMap() == null) {
            unionData.setPortMap(new ConcurrentHashMap<>());
        }
        Map<Integer, HangHaiPort> portMap = unionData.getPortMap();
        // 检查港口Map数量
        Map<Integer, HangHaiPortConfig> portConfigMap = config.getPortConfigMap();
        for (HangHaiPortConfig portConfig : portConfigMap.values()) {
            if (!portMap.containsKey(portConfig.getPortId())) {
                HangHaiPort port = new HangHaiPort();
                port.setActivityId(config.getActivityId());
                port.setUnionUid(unionData.getUnionUid());
                port.setPortId(portConfig.getPortId());
                port.setSpecId(portConfig.getSpecGoodsId());
                port.setLackId(0);
                port.setGoodsMap(new ConcurrentHashMap<>());
                portMap.put(port.getPortId(), port);
            }
        }

        // 清零数据
        for (HangHaiPort port : portMap.values()) {
            // 检查特产id
            HangHaiPortConfig portConfig = portConfigMap.get(port.getPortId());
            port.setSpecId(portConfig == null ? 0 : portConfig.getSpecGoodsId());
            // 清空紧缺id
            port.setLackId(0);
            // 重置上报
            port.setReportUserId(0L);
        }

        // 设置紧缺ID（都用一样的random，且方法内随机的参数都一样，结果就一样了）
        long resetTime = 0;
        Random lackRandom = getRandomByKey(config.getActivityId(), resetTime);
        initPortLackId(portMap, config, lackRandom);

        // 重置每个港口
        for (HangHaiPort hangHaiPort : portMap.values()) {

            // 生成一份货物
            Map<Integer, HangHaiGoods> goodsMap = new ConcurrentHashMap<>();
            for (HangHaiGoodsConfig goodsConfig : config.getGoodsConfigMap().values()) {
                HangHaiGoods goods = new HangHaiGoods();
                goods.setId(goodsConfig.getGoodsId());
                if (goodsConfig.getGoodsId() == hangHaiPort.getSpecId()) {
                    // 特产
                    goods.setCount(0);
                    goods.setPrice(calPrice(config, goods.getId(), 2, random));
                } else if (goodsConfig.getGoodsId() == hangHaiPort.getLackId()) {
                    // 紧缺
                    goods.setCount(0);
                    goods.setPrice(calPrice(config, goods.getId(), 2, random));
                } else {
                    // 普通
                    goods.setCount(0);
                    goods.setPrice(calPrice(config, goods.getId(), 2, random));
                }

                goodsMap.put(goods.getId(), goods);
            }

            // 货物加入港口
            hangHaiPort.setGoodsMap(goodsMap);
            hangHaiPort.setUpdateOption();
        }

        unionData.setLastResetTime(resetTime);
    }

    private boolean canResetData(List<Long> refreshTimeList, long lastResetTime) {
        // 活动开始前5秒才能刷新商会数据，否则数据可能没上传
        return System.currentTimeMillis() >= config.getActivityInfo().getBeginTime() * 1000 + config.getStartOperationTime() - DateHelper.SECOND_MILLIONS * 5
                && getLastResetTime(refreshTimeList) > lastResetTime;
    }

    private long getLastResetTime(List<Long> refreshTimeList) {
        long now = System.currentTimeMillis();
        long zeroTime = LocalDateTimeHelper.getZeroTimeTimeStamp(now);
        // 容错，没重置时间就0点重置
        if (refreshTimeList.isEmpty()) {
            return zeroTime;
        }
        // 第一个重置时间没到，返回昨天最后一个重置时间
        if (now < zeroTime + refreshTimeList.get(0)) {
            int size = refreshTimeList.size();
            return zeroTime - DateHelper.DAY_MILLIONS + refreshTimeList.get(size - 1);
        }
        // 正常计算
        long maxLastResetTime = zeroTime;
        for (Long tmpResetTime : refreshTimeList) {
            long resetTime = zeroTime + tmpResetTime;
            if (now >= resetTime) {
                maxLastResetTime = resetTime;
            } else {
                break;
            }
        }
        return maxLastResetTime;
    }

    private void resetStockData(HangHaiUnionData unionData) {

        synchronized (lock) {

            // 时间判断
            if (!canResetData(config.getRefreshStockTimeList(), unionData.getLastResetTime())) {
                return;
            }

            Random random = new Random();
            for (HangHaiPort hangHaiPort : unionData.getPortMap().values()) {
                for (HangHaiGoods goods : hangHaiPort.getGoodsMap().values()) {
                    if (hangHaiPort.getSpecId() == goods.getId()) {
                        // 特产
                        goods.setCount(calStockNum(config, goods.getId(), 1, unionData, random));
                    } else if (hangHaiPort.getLackId() == goods.getId()) {
                        // 紧缺
                        goods.setCount(calStockNum(config, goods.getId(), 3, unionData, random));
                    } else {
                        // 普通
                        goods.setCount(calStockNum(config, goods.getId(), 2, unionData, random));
                    }
                }
                hangHaiPort.setUpdateOption();
            }

            unionData.setLastResetTime(System.currentTimeMillis());

        }

        syncDataToUnion(unionData);
        syncReportTimesToUnion(unionData);
        for (HangHaiPort port : unionData.getPortMap().values()) {
            syncPortToUnion(port, unionData.getLastResetTime(), 2);
        }
    }

    private void checkUnionData(HangHaiUnionData unionData) {
        resetUnionData(unionData);
        resetStockData(unionData);
    }

    private void checkUserData(HangHaiUserData userData) {
        long now = System.currentTimeMillis();
        // 检查港口到达
        if (userData.getToPortId() != 0) {
            if (userData.getFinishTime() <= now) {
                synchronized (lock) {
                    if (userData.getToPortId() != 0 && userData.getFinishTime() <= now) {
                        userData.setPortId(userData.getToPortId());
                        userData.setToPortId(0);
                        setHadBuy(userData.getUserId(), false);
                    }
                    // 日志
                    CrossAutoLogMgr.add(new LogHangHaiMove(userData.getUserId(), userData.getActivityId(), userData.getPortId(), userData.getToPortId(), new Date(userData.getFinishTime()), 2));
                    // 同步船到达了
                    syncShipToUnionSlow(userData, false);
                }
            }
        }
        // 检查数据重置
        if (canResetData(config.getRefreshTimeList(), userData.getLastResetTime())) {
            userData.setReportRewardList(new ArrayList<>());
            userData.setLastResetTime(System.currentTimeMillis());
            // 同步玩家数据
            syncData(userData.getUserId(), userData, null);
        }
    }

    private int calStockNum(HangHaiConfig config, int goodsId, int type, HangHaiUnionData unionData, Random random) {
        int stock = 0;
        // 计算商会资金
        BigDecimal totalMoney = calUnionTotalMoney(unionData);
        // 保底资金
        int memberNum = Math.max(1, group.getMemberList(unionData.getUnionUid()).size());
        BigDecimal min1 = BigDecimal.valueOf(memberNum * config.getBaseMoney())
                .multiply(BigDecimal.valueOf(config.getUnionMinMoneyParamList().get(0)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP));
        BigDecimal min2 = getUnionMemberMaxMoney(unionData)
                .multiply(BigDecimal.valueOf(config.getUnionMinMoneyParamList().get(1)).divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_UP));
        if (totalMoney.compareTo(min1) < 0) {
            totalMoney = min1;
        }
        if (totalMoney.compareTo(min2) < 0) {
            totalMoney = min2;
        }

        HangHaiGoodsConfig goodsConfig = config.getGoodsConfigMap().get(goodsId);
        if (goodsConfig == null) {
            logger.error("CrossHangHaiMgr calStockNum error! no this goods! activityId={}, goodsId={}, unionUid={}", config.getActivityId(), goodsId, unionData.getUnionUid());
            return 0;
        }
        switch (type) {
            case 1: // 特产
                List<Integer> specList = config.getSpecStockParamList();
                BigDecimal param1 = BigDecimal.ZERO;
                for (int i = 0; i <= specList.get(1); i++) {
                    param1 = param1.add(
                            BigDecimal.valueOf(specList.get(2)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP).pow(i)
                    );
                }
                stock = BigDecimal.valueOf(specList.get(0)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP)
                        .multiply(totalMoney)
                        .multiply(param1)
                        .divide(
                                BigDecimal.valueOf(specList.get(3))
                                        .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP)
                                        .multiply(BigDecimal.valueOf(goodsConfig.getBasePrice()))
                                , 4, BigDecimal.ROUND_UP
                        )
                        .setScale(0, BigDecimal.ROUND_DOWN).intValue();
                break;
            case 2: // 普通
                List<Integer> commonList = config.getCommonStockParamList();
                BigDecimal param2 = BigDecimal.ZERO;
                for (int i = 0; i <= commonList.get(1); i++) {
                    param2 = param2.add(
                            BigDecimal.valueOf(commonList.get(2)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP).pow(i)
                    );
                }
                stock = BigDecimal.valueOf(commonList.get(0)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP)
                        .multiply(totalMoney)
                        .multiply(param2)
                        .divide(
                                BigDecimal.valueOf(commonList.get(3))
                                        .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP)
                                        .multiply(BigDecimal.valueOf(goodsConfig.getBasePrice()))
                                , 4, BigDecimal.ROUND_UP
                        )
                        .setScale(0, BigDecimal.ROUND_DOWN).intValue();
                break;
            case 3: // 紧缺
                stock = random.nextInt(config.getLackStockParamList().get(0)) + 1;
                break;
            case 4: // 用初始值
                stock = goodsConfig.getBaseNum();
        }
        return stock;
    }

    private static BigDecimal calPrice(HangHaiConfig config, int goodsId, int type, Random random) {
        HangHaiGoodsConfig goodsConfig = config.getGoodsConfigMap().get(goodsId);
        if (goodsConfig == null) {
            logger.error("CrossHangHaiMgr error on calPrice! No find goodsConfig!");
            return BigDecimal.ZERO;
        }
        BigDecimal price = BigDecimal.valueOf(goodsConfig.getBasePrice());
        switch (type) {
            case 1: // 特产
                List<Integer> specList = config.getSpecPriceParamList();
                price = price
                        .multiply(BigDecimal.valueOf(specList.get(0) + random.nextInt(specList.get(1) - specList.get(0))))
                        .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP);
                break;
            case 2: // 普通
                List<Integer> commonList = config.getCommonPriceParamList();
                price = price
                        .multiply(BigDecimal.valueOf(commonList.get(0) + random.nextInt(commonList.get(1) - commonList.get(0))))
                        .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP);
                break;
            case 3: // 紧缺
                List<Integer> lackList = config.getLackPriceParamList();
                price = price
                        .multiply(BigDecimal.valueOf(lackList.get(0) + random.nextInt(lackList.get(1) - lackList.get(0))))
                        .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP);
                break;
        }
        return price.setScale(0, BigDecimal.ROUND_HALF_UP);
    }

    private BigDecimal calUnionTotalMoney(HangHaiUnionData unionData) {
        BigDecimal total = BigDecimal.ZERO;
        for (long userId : group.getMemberList(unionData.getUnionUid())) {
            total = total.add(calUserTotalMoney(userId));
        }
        return total;
    }

    private BigDecimal getUnionMemberMaxMoney(HangHaiUnionData unionData) {
        BigDecimal max = BigDecimal.ONE;
        for (long userId : group.getMemberList(unionData.getUnionUid())) {
            BigDecimal tmp = calUserTotalMoney(userId);
            if (max.compareTo(tmp) < 0) {
                max = tmp;
            }
        }
        return max;
    }

    private BigDecimal calUserTotalMoney(long userId) {
        BigDecimal total = BigDecimal.ZERO;
        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return total;
        }
        // 现金
        total = total.add(userData.getMoney());
        // 货物价值
        for (HangHaiGoods goods : userData.getGoodsMap().values()) {
            total = total.add(
                    goods.getPrice().multiply(BigDecimal.valueOf(goods.getCount()))
            );
        }
        return total;
    }

    private void initPortLackId(Map<Integer, HangHaiPort> portMap, HangHaiConfig config, Random random) {
        List<Integer> noSetGoodsIdList = config.getGoodsConfigMap().values().stream().mapToInt(HangHaiGoodsConfig::getGoodsId).sorted().boxed().collect(Collectors.toList());
        List<Integer> portIdList = portMap.values().stream().mapToInt(HangHaiPort::getPortId).sorted().boxed().collect(Collectors.toList());
        Collections.shuffle(portIdList, random);
        for (int i = 0; i < portIdList.size() - 1; i++) {
            HangHaiPort curPort = portMap.get(portIdList.get(i));
            // 过滤到已设置紧缺货物（紧缺不重复）
            List<Integer> tmpGoodsList = new ArrayList<>(noSetGoodsIdList);
            // 移除掉自己特产（紧缺≠特产）
            tmpGoodsList.remove(Integer.valueOf(curPort.getSpecId()));
            // 移除掉互补港口的特产（不互补）
            int specId = curPort.getSpecId();
            for (int tmp = 0; tmp < portIdList.size(); tmp++) {
                HangHaiPort tmpPort = portMap.get(portIdList.get(tmp));
                if (tmpPort.getLackId() == specId) {
                    tmpGoodsList.remove(Integer.valueOf(tmpPort.getSpecId()));
                    break;
                }
            }
            // 剩下的是可选的了
            int goodsId = tmpGoodsList.get(random.nextInt(tmpGoodsList.size()));
            curPort.setLackId(goodsId);
            // 移除已选
            noSetGoodsIdList.remove(Integer.valueOf(goodsId));
        }
        // 特殊处理最后一个
        HangHaiPort lastPort = portMap.get(portIdList.get(portIdList.size() - 1));
        lastPort.setLackId(noSetGoodsIdList.get(0));
        if (lastPort.getSpecId() == lastPort.getLackId()) {
            // 从前面随便选一个交换
            Integer portId = portIdList.get(random.nextInt(portIdList.size() - 1));
            HangHaiPort randomPort = portMap.get(portId);
            int lackId = randomPort.getLackId();
            randomPort.setLackId(lastPort.getLackId());
            lastPort.setLackId(lackId);
        } else {
            // 判断最后一个有没有跟前面互补
            int errorPortId = -1;
            for (int tmp = 0; tmp < portIdList.size(); tmp++) {
                HangHaiPort tmpPort = portMap.get(portIdList.get(tmp));
                if (tmpPort.getPortId() != lastPort.getPortId() &&
                        tmpPort.getLackId() == lastPort.getSpecId() && tmpPort.getSpecId() == lastPort.getLackId()) {
                    errorPortId = tmpPort.getPortId();
                    break;
                }
            }
            if (errorPortId != -1) {
                List<Integer> toChangePortIdList = new ArrayList<>(portIdList);
                toChangePortIdList.remove(Integer.valueOf(errorPortId));
                toChangePortIdList.remove(Integer.valueOf(lastPort.getPortId()));
                int toChangePortId = toChangePortIdList.get(random.nextInt(toChangePortIdList.size()));
                HangHaiPort toChangePort = portMap.get(toChangePortId);

                int lackId = toChangePort.getLackId();
                toChangePort.setLackId(lastPort.getLackId());
                lastPort.setLackId(lackId);
            }
        }
    }

    private Random getRandomByKey(int activityId, long time) {
        return new Random((activityId + "yanqu666" + time).hashCode());
    }

    private int report(long userId, String unionUid) {

        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }

        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }

        synchronized (lock) {

            // 港口校验
            if (userData.getToPortId() != 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_NO_IN_THIS_PORT;
            }

            HangHaiPort port = unionData.getPortMap().get(userData.getPortId());
            if (port.getReportUserId() != 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_PORT_HAD_REPORT;
            }
            port.setReportUserId(userId);

            // 同步港口
            syncPortToUnion(port, unionData.getLastResetTime(), 3);

            // 同步上报次数
            syncReportTimesToUnion(unionData);

            CrossAutoLogMgr.add(new LogHangHaiReport(
                    userData.getUserId(),
                    userData.getActivityId(),
                    unionData.getUnionUid(),
                    userData.getPortId()
            ));

        }

        return 0;
    }

    private int buy(long userId, String unionUid, HangHaiActivityProto.HangHaiBuyGoodsReqMsg reqMsg) {
        int goodsId = reqMsg.getGoodsId();
        int price = reqMsg.getPrice();
        int count = reqMsg.getCount();
        boolean buy = reqMsg.getIsBuy();

        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }
        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }

        synchronized (lock) {

            // 港口校验
            if (userData.getToPortId() != 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_NO_IN_THIS_PORT;
            }
            // 道具校验
            if (!config.getGoodsConfigMap().containsKey(goodsId)) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_GOODS_NO_FIND;
            }
            HangHaiPort port = unionData.getPortMap().get(userData.getPortId());
            HangHaiGoods unionGoods = port.getGoodsMap().get(goodsId);
            if (unionGoods == null) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_GOODS_NO_FIND;
            }
            // 价格校验
            if (unionGoods.getPrice().intValue() != price) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_PRICE_HAD_REFRESH;
            }
            // 数量校验
            if (count <= 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_COUNT_BELOW_ZERO;
            }

            // 设置0值
            if (null == userData.getGoodsMap().get(goodsId)) {
                HangHaiGoods goods = new HangHaiGoods();
                goods.setId(goodsId);
                goods.setPrice(BigDecimal.ZERO);
                goods.setCount(0);
                userData.getGoodsMap().put(goodsId, goods);
                userData.setUpdateOption();
            }
            HangHaiGoods userGoods = userData.getGoodsMap().get(goodsId);

            String logOldMoney = userData.getMoney().toString();
            String logOldCost = userGoods.getPrice().toString();
            String logChangeMoney = "-1";

            // 【购买】
            if (buy) {
                if (unionGoods.getCount() < count) {
                    return GameErrorCode.E_HANGHAI_ACTIVITY_STOCK_NO_ENOUGH;
                }
                BigDecimal costMoney = unionGoods.getPrice().multiply(BigDecimal.valueOf(count));
                logChangeMoney = costMoney.toString();
                if (userData.getMoney().compareTo(costMoney) < 0) {
                    return GameErrorCode.E_HANGHAI_ACTIVITY_MONEY_NO_ENOUGH;
                }

                // 计算货物总价值
                BigDecimal oldMoney = userGoods.getPrice().multiply(BigDecimal.valueOf(userGoods.getCount()));
                BigDecimal newMoney = oldMoney.add(costMoney);

                // 数量资金修改
                unionGoods.setCount(unionGoods.getCount() - count);
                port.setUpdateOption();
                userGoods.setCount(userGoods.getCount() + count);
                userData.setUpdateOption();
                userData.setMoney(userData.getMoney().subtract(costMoney));

                // 计算货物成本
                userGoods.setPrice(newMoney.divide(BigDecimal.valueOf(userGoods.getCount()), 4, BigDecimal.ROUND_UP));
                userData.setUpdateOption();

                syncPortToUnion(port, unionData.getLastResetTime(), 1);
            } else {
                // 【卖出】
                if (userGoods.getCount() < count) {
                    return GameErrorCode.E_HANGHAI_ACTIVITY_GOODS_COUNT_NO_ENOUGH;
                }

                // 获取船加成
                HangHaiShipConfig shipConfig = null;
                for (HangHaiShipConfig tmp : config.getShipConfigList()) {
                    if (tmp.getLevel() == userData.getShipLevel()) {
                        shipConfig = tmp;
                        break;
                    }
                }
                int add = shipConfig == null ? 0 : shipConfig.getAddition();

                // 船加成利润（不为负） = （ 卖出价格 - 成本 ）x 数量 x 船加成
                BigDecimal profit = unionGoods.getPrice().subtract(userGoods.getPrice()).multiply(BigDecimal.valueOf(count))
                        .multiply(BigDecimal.valueOf(add)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_UP);
                if (profit.compareTo(BigDecimal.ZERO) < 0) {
                    profit = BigDecimal.ZERO;
                }

                // 最终收入 = 卖出价格 + 成本
                BigDecimal getMoney = unionGoods.getPrice().multiply(BigDecimal.valueOf(count))
                        .add(profit);

                logChangeMoney = getMoney.toString();

                // 修改数量、资金
                userGoods.setCount(userGoods.getCount() - count);
                userData.setUpdateOption();
                if (userGoods.getCount() <= 0) {
                    userData.getGoodsMap().remove(userGoods.getId());
                    userData.setUpdateOption();
                }
                userData.setMoney(userData.getMoney().add(getMoney));
            }

            String logNewMoney = userData.getMoney().toString();
            String logNewCost = userGoods.getPrice().toString();
            int logGoodsType = goodsId == port.getLackId() ? 3 :
                    goodsId == port.getSpecId() ? 1 : 2;
            HangHaiGoodsConfig goodsConfig = config.getGoodsConfigMap().get(goodsId);
            String logPriceUp = "0";
            if (goodsConfig != null) {
                logPriceUp = unionGoods.getPrice().subtract(BigDecimal.valueOf(goodsConfig.getBasePrice())).divide(unionGoods.getPrice(), 4, BigDecimal.ROUND_HALF_UP).toString();
            }

            CrossAutoLogMgr.add(new LogHangHaiBuy(
                    userData.getUserId(),
                    userData.getActivityId(),
                    buy,
                    logOldMoney,
                    logChangeMoney,
                    logNewMoney,
                    goodsId,
                    count,
                    unionGoods.getPrice().toString(),
                    logPriceUp,
                    logGoodsType,
                    logOldCost,
                    logNewCost,
                    port.getPortId()
            ));

            BigDecimal totalMoney = calUserTotalMoney(userData.getUserId());

            HangHaiActivityProto.CrossHangHaiNotifyMoneyConditionMsg.Builder notifyBuilder = HangHaiActivityProto.CrossHangHaiNotifyMoneyConditionMsg.newBuilder();
            notifyBuilder.setActivityId(config.getActivityId());
            notifyBuilder.setTotalMoney(totalMoney.longValue());
            sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_NOTIFY_MONEY_CONDITION_FROM_CROSS, notifyBuilder, userId);

            setHadBuy(userId, true);
        }
        return 0;
    }

    private int move(long userId, String unionUid, HangHaiActivityProto.HangHaiMoveShipReqMsg reqMsg) {
        int toPortId = reqMsg.getPortId();
        boolean forceMove = reqMsg.getForceMove();

        // 校验去往的港口信息是否在配置中
        if (!config.getPortConfigMap().containsKey(toPortId)) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_PARAM_ERROR;
        }

        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }
        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }

        synchronized (lock) {

            long now = System.currentTimeMillis();
            if (userData.getToPortId() != 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_SHIP_HAD_START;
            }
            if (userData.getPortId() == toPortId) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_PORT_HAD_ARRIVE;
            }

            boolean hadBuy = getHadBuy(userData.getUserId());
            if (!forceMove && !hadBuy) {
                // 不是强制移动，且未购买   =>   无法移动
                return GameErrorCode.E_HANGHAI_ACTIVITY_NO_TRADE;
            } else if (forceMove && hadBuy) {
                // 强制移动，且已购买   =>   客户单瞎传，打个日志就好
                logger.error("HangHai move param bug! activityId={}, userId={}, curPort={}, wantPort={}, forceMove but hadBuy!",
                        config.getActivityId(), userData.getUserId(), userData.getPortId(), toPortId);
            }

            userData.setToPortId(toPortId);
            userData.setFinishTime(now + config.getMoveTime());
        }

        CrossAutoLogMgr.add(new LogHangHaiMove(userData.getUserId(), userData.getActivityId(), userData.getPortId(), userData.getToPortId(), new Date(userData.getFinishTime()),
                forceMove ? 4 : 1));

        syncShipToUnion(unionUid, userData, false);
        return 0;
    }

    private int upgradeShip(long userId, String unionUid, HangHaiActivityProto.CrossHangHaiUpgradeShipReqMsg reqMsg) {

        int shipLevel = reqMsg.getShipLevel();

        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }

        synchronized (lock) {
            if (userData.getShipLevel() != shipLevel) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_REQ_FREQUENTLY;
            }
            userData.setShipLevel(userData.getShipLevel() + 1);
        }

        return 0;
    }

    private int getReportReward(long userId, String unionUid, HangHaiActivityProto.GetHangHaiReportRewardReqMsg reqMsg) {
        int times = reqMsg.getReportTimes();

        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }

        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }

        String reward = "";

        synchronized (lock) {

            if (userData.getReportRewardList().contains(times)) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_HAD_GET_REPORT_REWARD;
            }

            int reportTimes = 0;
            for (HangHaiPort port : unionData.getPortMap().values()) {
                if (port.getReportUserId() != 0) {
                    reportTimes++;
                }
            }

            if (reportTimes < times) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_REPORT_TIMES_NO_ENOUGH;
            }

            int index = config.getReportTimesList().indexOf(times);
            if (index < 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_PARAM_ERROR;
            }
            reward = config.getReportRewardList().get(index);
            if (StringUtils.isNullOrEmpty(reward)) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_PARAM_ERROR;
            }

            userData.getReportRewardList().add(times);
            userData.setUpdateOption();

            CrossAutoLogMgr.add(new LogHangHaiReportReward(
                    userData.getUserId(),
                    userData.getActivityId(),
                    unionData.getUnionUid(),
                    times,
                    reward
            ));
        }

        HangHaiActivityProto.GetHangHaiReportRewardRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiReportRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setUserData(parseHangHaiUserDataMsg(userData));
        builder.setReward(reward);
        sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_GET_REPORT_REWARD_FROM_CROSS, builder, userId);
        return 0;
    }

    private int speedUp(long userId, String unionUid, HangHaiActivityProto.HangHaiSpeedUpReqMsg reqMsg) {
        int times = reqMsg.getTimes();

        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }
        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }

        int realTimes = 0;

        synchronized (lock) {
            // 是否前进中
            if (userData.getToPortId() == 0) {
                return GameErrorCode.E_HANGHAI_ACTIVITY_SHIP_NO_START;
            }

            // 获取加速效果
            GoodsInfo goodsInfo = GoodsMgr.getGoodsInfo(config.getSpeedUpItemId());
            long speedUpTime = goodsInfo.getParamList().get(0).longValue() * 60 * 1000;

            // 计算走到结束需要多少次
            long now = System.currentTimeMillis();
            long finishTime = userData.getFinishTime();
            int needTimes = BigDecimal.valueOf(finishTime - now).divide(BigDecimal.valueOf(speedUpTime), 0, BigDecimal.ROUND_UP).intValue();

            // 计算最终使用次数
            realTimes = Math.min(times, needTimes);

            // 修改时间
            userData.setFinishTime(userData.getFinishTime() - realTimes * speedUpTime);

            // 日志
            CrossAutoLogMgr.add(new LogHangHaiMove(userData.getUserId(), userData.getActivityId(), userData.getPortId(), userData.getToPortId(), new Date(userData.getFinishTime()), 3));

            // 检查数据重置
            checkUserData(userData);

            // 同步数据
            syncShipToUnion(unionUid, userData, false);

        }

        // 返回
        HangHaiActivityProto.HangHaiSpeedUpRespMsg.Builder respMsg = HangHaiActivityProto.HangHaiSpeedUpRespMsg.newBuilder();
        respMsg.setRet(0);
        respMsg.setUserData(parseHangHaiUserDataMsg(userData));

        HangHaiActivityProto.CrossHangHaiSpeedUpRespMsg.Builder builder = HangHaiActivityProto.CrossHangHaiSpeedUpRespMsg.newBuilder();
        builder.setResp(respMsg);
        builder.setTimes(times);
        builder.setRealTimes(realTimes);
        sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_SPEED_UP_FROM_CROSS, builder, userId);
        return 0;
    }

    private int getData(long userId, String unionUid) {
        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }
        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }

        HangHaiActivityProto.GetHangHaiDataRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiDataRespMsg.newBuilder();
        builder.setRet(0);
        builder.setUserData(parseHangHaiUserDataMsg(userData));
        builder.setUnionData(parseHangHaiUnionDataMsg(unionData));
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_DATA, builder, userId);
        return 0;
    }

    private int getPort(long userId, String unionUid) {
        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION;
        }
        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK;
        }
        if (userData.getToPortId() != 0) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_NO_IN_THIS_PORT;
        }

        HangHaiPort port = unionData.getPortMap().get(userData.getPortId());
        HangHaiActivityProto.HangHaiPortMsg.Builder portMsg = parseHangHaiPortMsg(port, unionData.getLastResetTime());

        HangHaiActivityProto.GetHangHaiPortRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiPortRespMsg.newBuilder();
        builder.setRet(0);
        builder.setPort(portMsg);
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_PORT_DATA, builder, userId);
        return 0;
    }

    private int getUnionMember(long userId, String unionUid, HangHaiActivityProto.GetHangHaiUnionMemberDataReqMsg reqMsg) {
        HangHaiUserData otherUserData = getUserData(reqMsg.getUserId());
        if (otherUserData == null) {
            return GameErrorCode.E_HANGHAI_ACTIVITY_PARAM_ERROR;
        }

        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(reqMsg.getUserId());
        if (userBaseInfo == null) {
            userBaseInfo = new UserBaseInfo();
        }

        HangHaiActivityProto.GetHangHaiUnionMemberDataRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiUnionMemberDataRespMsg.newBuilder();
        builder.setRet(0);
        builder.setPlayer(PlayerBasePb.parsePlayerBaseTempMsg(userBaseInfo));
        builder.setUserData(parseHangHaiUserDataMsg(otherUserData));
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_UNION_MEMBER_DATA, builder, userId);
        return 0;
    }

    // ======================================== CMD请求 ======================================

    public void loginByCmd(long userId, String unionUid, HangHaiActivityProto.CrossHangHaiLoginReqMsg reqMsg) {
        // 更新userInfo
        CrossUserMgr.updateUserBaseInfo(userId, PlayerBasePb.parseBaseUserInfo(reqMsg.getPlayer()));
        // 生成数据
        loginCreateUserData(userId, reqMsg);

        // 可能为空！！！
        HangHaiUserData userData = getUserData(userId);
        HangHaiUnionData unionData = getUnionData(unionUid);

        // 同步玩家、商会数据
        syncData(userId, userData, unionData);
        // 同步上报次数
        int times = 0;
        if (unionData != null) {
            for (HangHaiPort port : unionData.getPortMap().values()) {
                if (port.getReportUserId() != 0) times++;
            }
        }
        syncReportTimes(userId, times);

        // 更新成就
        HangHaiActivityProto.CrossHangHaiNotifyMoneyConditionMsg.Builder builder = HangHaiActivityProto.CrossHangHaiNotifyMoneyConditionMsg.newBuilder().setActivityId(config.getActivityId()).setTotalMoney(calUserTotalMoney(userId).longValue());
        sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_NOTIFY_MONEY_CONDITION_FROM_CROSS, builder, userId);
    }

    public void getDataByCmd(long userId, String unionUid) {
        int ret = getData(userId, unionUid);
        if (ret != 0) {
            HangHaiActivityProto.GetHangHaiDataRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiDataRespMsg.newBuilder();
            builder.setRet(ret);
            sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_DATA, builder, userId);
        }
    }

    public void getPortByCmd(long userId, String unionUid) {
        int ret = getPort(userId, unionUid);
        if (ret != 0) {
            HangHaiActivityProto.GetHangHaiPortRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiPortRespMsg.newBuilder();
            builder.setRet(ret);
            sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_PORT_DATA, builder, userId);
        }
    }

    public void reportByCmd(long userId, String unionUid) {
        int ret = report(userId, unionUid);
        HangHaiActivityProto.HangHaiReportRespMsg.Builder builder = HangHaiActivityProto.HangHaiReportRespMsg.newBuilder();
        builder.setRet(ret);
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_REPORT, builder, userId);
    }

    public void buyByCmd(long userId, String unionUid, HangHaiActivityProto.HangHaiBuyGoodsReqMsg reqMsg) {
        int ret = buy(userId, unionUid, reqMsg);
        HangHaiActivityProto.HangHaiBuyGoodsRespMsg.Builder builder = HangHaiActivityProto.HangHaiBuyGoodsRespMsg.newBuilder();
        builder.setRet(ret);
        HangHaiUserData userData = getUserData(userId);
        if (userData != null) {
            builder.setUserData(parseHangHaiUserDataMsg(userData));
        }
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_BUY_GOODS, builder, userId);
    }

    public void moveByCmd(long userId, String unionUid, HangHaiActivityProto.HangHaiMoveShipReqMsg reqMsg) {
        int ret = move(userId, unionUid, reqMsg);
        HangHaiActivityProto.HangHaiMoveShipRespMsg.Builder builder = HangHaiActivityProto.HangHaiMoveShipRespMsg.newBuilder();
        builder.setRet(ret);
        HangHaiUserData userData = getUserData(userId);
        if (userData != null) {
            builder.setUserData(parseHangHaiUserDataMsg(userData));
        }
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_MOVE_SHIP, builder, userId);
    }

    public void getReportDataByCmd(long userId, String unionUid) {
        HangHaiActivityProto.GetHangHaiReportDataRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiReportDataRespMsg.newBuilder();
        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            builder.setRet(GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNION);
        } else {
            builder.setRet(0);
            for (HangHaiPort port : unionData.getPortMap().values()) {
                if (port.getReportUserId() != 0) {
                    builder.addReportDataList(parseHangHaiReportDataMsg(port, unionData.getLastResetTime()));
                }
            }
        }
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_REPORT_DATA, builder, userId);
    }

    public void getUnionMemberByCmd(long userId, String unionUid, HangHaiActivityProto.GetHangHaiUnionMemberDataReqMsg reqMsg) {
        int ret = getUnionMember(userId, unionUid, reqMsg);
        if (ret != 0) {
            HangHaiActivityProto.GetHangHaiUnionMemberDataRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiUnionMemberDataRespMsg.newBuilder();
            builder.setRet(0);
            sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_UNION_MEMBER_DATA, builder, userId);
        }
    }

    public void getShipLevelByCmd(long userId, String unionUid) {
        HangHaiUserData userData = getUserData(userId);
        if (userData == null) {

            HangHaiActivityProto.HangHaiUpgradeShipRespMsg.Builder builder = HangHaiActivityProto.HangHaiUpgradeShipRespMsg.newBuilder();
            builder.setRet(GameErrorCode.E_HANGHAI_ACTIVITY_NO_UNLOCK);
            sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_UPGRADE_SHIP, builder, userId);

        } else {

            HangHaiActivityProto.CrossHangHaiGetUpgradeShipLevelRespMsg.Builder builder = HangHaiActivityProto.CrossHangHaiGetUpgradeShipLevelRespMsg.newBuilder();
            builder.setShipLevel(userData.getShipLevel());
            sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_GET_UPGRADE_SHIP_LEVEL_FROM_CROSS, builder, userId);

        }
    }

    public void upgradeShipByCmd(long userId, String unionUid, HangHaiActivityProto.CrossHangHaiUpgradeShipReqMsg reqMsg) {
        int ret = upgradeShip(userId, unionUid, reqMsg);

        HangHaiActivityProto.HangHaiUpgradeShipRespMsg.Builder respMsg = HangHaiActivityProto.HangHaiUpgradeShipRespMsg.newBuilder();
        respMsg.setRet(ret);
        HangHaiUserData userData = getUserData(userId);
        if (userData != null) {
            respMsg.setUserData(parseHangHaiUserDataMsg(userData));
        }

        HangHaiActivityProto.CrossHangHaiUpgradeShipRespMsg.Builder builder = HangHaiActivityProto.CrossHangHaiUpgradeShipRespMsg.newBuilder();
        builder.setResp(respMsg);
        builder.setPreReduceCost(reqMsg.getPreReduceCost());
        sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_UPGRADE_SHIP_FROM_CROSS, builder, userId);
    }

    public void getReportRewardByCmd(long userId, String unionUid, HangHaiActivityProto.GetHangHaiReportRewardReqMsg reqMsg) {
        int ret = getReportReward(userId, unionUid, reqMsg);
        if (ret != 0) {
            HangHaiActivityProto.GetHangHaiReportRewardRespMsg.Builder builder = HangHaiActivityProto.GetHangHaiReportRewardRespMsg.newBuilder();
            builder.setRet(ret);
            HangHaiUserData userData = getUserData(userId);
            if (userData != null) {
                builder.setUserData(parseHangHaiUserDataMsg(userData));
            }
            sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_GET_REPORT_REWARD, builder, userId);
        }
    }

    public void speedUpByCmd(long userId, String unionUid, HangHaiActivityProto.HangHaiSpeedUpReqMsg reqMsg) {
        int ret = speedUp(userId, unionUid, reqMsg);
        if (ret != 0) {
            HangHaiActivityProto.HangHaiSpeedUpRespMsg.Builder respMsg = HangHaiActivityProto.HangHaiSpeedUpRespMsg.newBuilder();
            respMsg.setRet(0);
            HangHaiUserData userData = getUserData(userId);
            if (userData != null) {
                respMsg.setUserData(parseHangHaiUserDataMsg(userData));
            }

            HangHaiActivityProto.CrossHangHaiSpeedUpRespMsg.Builder builder = HangHaiActivityProto.CrossHangHaiSpeedUpRespMsg.newBuilder();
            builder.setResp(respMsg);
            builder.setTimes(reqMsg.getTimes());
            builder.setRealTimes(0);
            sendPacket(GameProtocol.S_HANGHAI_ACTIVITY_SPEED_UP_FROM_CROSS, builder, userId);
        }
    }

    public void unionActionByCmd(HangHaiActivityProto.CrossHangHaiUnionActionReqMsg reqMsg) {
        long userId = reqMsg.getUserId();
        String unionUid = reqMsg.getUnionUid();
        int type = reqMsg.getType();

        if (type == 1) {
            joinUnionAction(userId, unionUid, reqMsg);
        } else if (type == 2) {
            quitUnionAction(userId, unionUid);
        }
    }


    // ======================================== 商会成员相关 ======================================

    private void updateUnionMember(String unionUid, List<Long> idList, int type) {
        HangHaiUnionData unionData = getUnionData(unionUid);
        if (unionData == null) {
            return;
        }

        Set<Long> oldList = new HashSet<>(group.getMemberList(unionUid));

        // 同步
        List<Long> syncList = new ArrayList<>();
        for (long userId : idList) {
            if (type == 2 && !oldList.contains(userId)) {
                syncList.add(userId);
            } else if (type == 3 && oldList.contains(userId)) {
                syncList.add(userId);
            }
        }
        for (Long userId : syncList) {
            HangHaiUserData userData = getUserData(userId);
            if (userData != null) {
                syncShipToUnion(unionUid, userData, type == 3);
            }
        }
    }

    private void joinUnionAction(long userId, String unionUid, HangHaiActivityProto.CrossHangHaiUnionActionReqMsg reqMsg) {
        updateUnionMember(unionUid, Collections.singletonList(userId), 2);

        HangHaiUserData userData = getUserData(userId);
        HangHaiUnionData unionData = getUnionData(unionUid);

        if (userData != null && unionData != null) {
            // 同步数据
            syncData(userId, userData, unionData);

            // 同步上报数据
            int reportTimes = 0;
            for (HangHaiPort port : unionData.getPortMap().values()) {
                if (port.getReportUserId() != 0) reportTimes++;
            }
            syncReportTimes(userId, reportTimes);

            // 把船同步给商会玩家
            syncShipToUnion(unionUid, userData, false);
        }
    }

    private void quitUnionAction(long userId, String unionUid) {
        updateUnionMember(unionUid, Collections.singletonList(userId), 3);
    }

    public void unionUpdate(UnionActivityGroupUnion union) {
        List<Long> userIdList = new ArrayList<>(union.getUserMap().keySet());
        updateUnionMember(union.getUnionUid(), userIdList, 1);
    }

    // ======================================== SYNC ======================================

    public void syncReportTimes(long userId, int times) {
        HangHaiActivityProto.SyncHangHaiReportTimesRespMsg.Builder builder = HangHaiActivityProto.SyncHangHaiReportTimesRespMsg.newBuilder();
        builder.setTotalTimes(times);
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_SYNC_REPORT_TIMES, builder, userId);
    }

    public void syncDataToUnion(HangHaiUnionData unionData) {
        for (Long userId : group.getMemberList(unionData.getUnionUid())) {
            syncData(userId, null, unionData);
        }
    }

    public void syncData(long userId, HangHaiUserData userData, HangHaiUnionData unionData) {
        HangHaiActivityProto.SyncHangHaiDataRespMsg.Builder builder = HangHaiActivityProto.SyncHangHaiDataRespMsg.newBuilder();
        if (userData != null) {
            builder.setUserData(parseHangHaiUserDataMsg(userData));
        }
        if (unionData != null) {
            builder.setUnionData(parseHangHaiUnionDataMsg(unionData));
        }
        sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_SYNC_DATA, builder, userId);
    }

    // type 1买卖，2重置，3上报，4刷活动
    public void syncPortToUnion(HangHaiPort port, long refreshTime, int type) {
        HangHaiActivityProto.SyncHangHaiPortRespMsg.Builder builder = HangHaiActivityProto.SyncHangHaiPortRespMsg.newBuilder();
        builder.setPortData(parseHangHaiPortMsg(port, refreshTime));
        builder.setType(type);

        for (Long userId : group.getMemberList(port.getUnionUid())) {
            HangHaiUserData userData = getUserData(userId);
            if (userData != null && userData.getPortId() == port.getPortId()) {
                sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_SYNC_GOODS, builder, userId);
            }
        }
    }

    public void syncReportTimesToUnion(HangHaiUnionData unionData) {
        int times = 0;
        for (HangHaiPort port : unionData.getPortMap().values()) {
            if (port.getReportUserId() != 0) times++;
        }

        HangHaiActivityProto.SyncHangHaiReportTimesRespMsg.Builder builder = HangHaiActivityProto.SyncHangHaiReportTimesRespMsg.newBuilder();
        builder.setTotalTimes(times);

        for (Long userId : group.getMemberList(unionData.getUnionUid())) {
            syncReportTimes(userId, times);
        }
    }

    /**
     * 这个同步还要找商会ID会比较慢
     */
    public void syncShipToUnionSlow(HangHaiUserData userData, boolean isRemove) {
        String unionUid = "";
        long userId = userData.getUserId();
        for (HangHaiUnionData unionData : group.getUnionDataMap().values()) {
            if (group.getMemberList(unionData.getUnionUid()).contains(userId)) {
                unionUid = unionData.getUnionUid();
                break;
            }
        }

        // 找到商会才能同步啊
        if (!"".equals(unionUid)) {
            syncShipToUnion(unionUid, userData, isRemove);
        }
    }

    public void syncShipToUnion(String unionUid, HangHaiUserData userData, boolean isRemove) {
        HangHaiActivityProto.SyncHangHaiShipRespMsg.Builder builder = HangHaiActivityProto.SyncHangHaiShipRespMsg.newBuilder();
        builder.setShipData(parseHangHaiShipMsg(userData));
        builder.setIsRemove(isRemove);
        for (Long userId : group.getMemberList(unionUid)) {
            sendPacket(ClientProtocol.U_HANGHAI_ACTIVITY_SYNC_SHIP, builder, userId);
        }
    }

    public void sendPacket(int code, AbstractMessage.Builder<?> messageBuilder, long userId) {
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (userBaseInfo == null) {
            return;
        }
        YanQuMessage message = YanQuMessageUtils.buildMessage(code, messageBuilder);
        MessageHelper.sendPacket(userBaseInfo.getServerId(), userId, message);
    }

    // ======================================== PB ======================================

    public HangHaiActivityProto.HangHaiUserDataMsg.Builder parseHangHaiUserDataMsg(HangHaiUserData userData) {
        HangHaiActivityProto.HangHaiUserDataMsg.Builder builder = HangHaiActivityProto.HangHaiUserDataMsg.newBuilder();
        builder.setMoney(userData.getMoney().longValue());
        for (HangHaiGoods hangHaiGoods : userData.getGoodsMap().values()) {
            builder.addGoodsList(parseHangHaiGoodsMsg(hangHaiGoods));
        }
        builder.setShipData(parseHangHaiShipMsg(userData));
        builder.addAllReportRewardList(userData.getReportRewardList());
        return builder;
    }

    public HangHaiActivityProto.HangHaiGoodsMsg.Builder parseHangHaiGoodsMsg(HangHaiGoods hangHaiGoods) {
        HangHaiActivityProto.HangHaiGoodsMsg.Builder builder = HangHaiActivityProto.HangHaiGoodsMsg.newBuilder();
        builder.setGoodsId(hangHaiGoods.getId());
        builder.setCount(hangHaiGoods.getCount());
        builder.setPrice(hangHaiGoods.getPrice().intValue());
        return builder;
    }

    public HangHaiActivityProto.HangHaiShipMsg.Builder parseHangHaiShipMsg(HangHaiUserData userData) {
        HangHaiActivityProto.HangHaiShipMsg.Builder builder = HangHaiActivityProto.HangHaiShipMsg.newBuilder();
        builder.setUserId(userData.getUserId());
        builder.setPortId(userData.getPortId());
        builder.setToPortId(userData.getToPortId());
        builder.setFinishTime(userData.getFinishTime());
        builder.setShipLevel(userData.getShipLevel());
        builder.setNickname("");
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userData.getUserId());
        if (userBaseInfo != null) {
            builder.setNickname(userBaseInfo.getNickName());
        }
        builder.setTotalMoney(calUserTotalMoney(userData.getUserId()).longValue());
        return builder;
    }

    public HangHaiActivityProto.HangHaiUnionDataMsg.Builder parseHangHaiUnionDataMsg(HangHaiUnionData unionData) {
        HangHaiActivityProto.HangHaiUnionDataMsg.Builder builder = HangHaiActivityProto.HangHaiUnionDataMsg.newBuilder();

        for (HangHaiPort hangHaiPort : unionData.getPortMap().values()) {
            builder.addPortList(parseHangHaiBasePortMsg(hangHaiPort));
        }

        for (Long userId : group.getMemberList(unionData.getUnionUid())) {
            HangHaiUserData userData = getUserData(userId);
            if (userData != null) {
                builder.addShipList(parseHangHaiShipMsg(userData));
            }
        }
        return builder;
    }

    public HangHaiActivityProto.HangHaiBasePortMsg.Builder parseHangHaiBasePortMsg(HangHaiPort portData) {
        HangHaiActivityProto.HangHaiBasePortMsg.Builder builder = HangHaiActivityProto.HangHaiBasePortMsg.newBuilder();
        builder.setPortId(portData.getPortId());
        HangHaiGoods specGoods = portData.getGoodsMap().get(portData.getSpecId());
        builder.setSpecGoods(parseHangHaiGoodsMsg(specGoods));
        return builder;
    }

    public HangHaiActivityProto.HangHaiPortMsg.Builder parseHangHaiPortMsg(HangHaiPort portData, long refreshTime) {
        HangHaiActivityProto.HangHaiPortMsg.Builder builder = HangHaiActivityProto.HangHaiPortMsg.newBuilder();
        builder.setPortId(portData.getPortId());
        builder.setSpecId(portData.getSpecId());
        builder.setLackId(portData.getLackId());
        for (HangHaiGoods hangHaiGoods : portData.getGoodsMap().values()) {
            builder.addGoodsList(parseHangHaiGoodsMsg(hangHaiGoods));
        }
        builder.setHadReport(portData.getReportUserId() != 0);
        builder.setRefreshTime(refreshTime);
        return builder;
    }

    public HangHaiActivityProto.HangHaiReportDataMsg.Builder parseHangHaiReportDataMsg(HangHaiPort port, long refreshTime) {
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(port.getReportUserId());

        String nickname = userBaseInfo == null ? "" : userBaseInfo.getNickName();
        HangHaiActivityProto.HangHaiReportDataMsg.Builder builder = HangHaiActivityProto.HangHaiReportDataMsg.newBuilder();
        builder.setPort(parseHangHaiPortMsg(port, refreshTime));
        builder.setNickName(nickname);
        return builder;
    }
}
